home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / delicious_bookmarks-2.0.64-fx.xpi / components / ssrDelicious.js < prev    next >
Text File  |  2008-06-19  |  70KB  |  1,909 lines

  1. // $Id: //DeliciousXT/Main/FFXT/del2alpha/components/ssrDelicious.js#17 $
  2.  
  3. /***********************************************************
  4. constants
  5. ***********************************************************/
  6.  
  7. // reference to the interface defined in nsISocialStore.idl
  8. const nsISocialStore = Components.interfaces.nsISocialStore;
  9.  
  10. // reference to the required base interface that all components must support
  11. const nsISupports = Components.interfaces.nsISupports;
  12.  
  13. // UUID uniquely identifying our component
  14. // You can get from: http://kruithof.xs4all.nl/uuid/uuidgen here
  15. const CLASS_ID = Components.ID("{983c8b92-39a6-40dc-8289-7087c1272e6e}");
  16.  
  17. // description
  18. const CLASS_NAME = "Access del.icio.us posts.";
  19.  
  20. // textual unique identifier
  21. const CONTRACT_ID = "@yahoo.com/socialstore/delicious;1";
  22.  
  23. //const DEL_PREFIX = 'https://api.preview.delicious.com';
  24. //const DEL_PREFIX = 'http://api.qa.delicious.com';
  25. //const DEL_PREFIX = 'http://api.staging.delicious.com';
  26. const DEL_PREFIX = 'https://api.del.icio.us';
  27.  
  28. const DEL_ALL_URL = DEL_PREFIX + '/v1/posts/all?';
  29. const DEL_UPDATE_URL = DEL_PREFIX + '/v1/posts/update?inboxnew=1';
  30. const DEL_GETBOOKMARKS_URL = DEL_PREFIX + '/v1/posts/get?';
  31. const DEL_ADDBOOKMARK_URL = DEL_PREFIX + '/v1/posts/add?';
  32. const DEL_DELETEBOOKMARK_URL = DEL_PREFIX + '/v1/posts/delete?';
  33. const DEL_SUGGEST_URL = DEL_PREFIX + '/v1/posts/suggest?format=json&sort=popular&popular_count=10&url=';
  34. const DEL_IMPORT_URL = DEL_PREFIX + '/v1/import/upload?format=json';
  35. const DEL_IMPORT_STATUS_URL = DEL_PREFIX + '/v1/import/status?format=json';
  36.  
  37. const DEL_RECENT_URL = DEL_PREFIX + '/v1/posts/recent';
  38. const DEL_POPULAR_URL = 'http://del.icio.us/rss/popular/';
  39.  
  40. const DEL_ALL_BUNDLES = DEL_PREFIX + '/v1/tags/bundles/all';
  41. const DEL_SET_BUNDLE = DEL_PREFIX + '/v1/tags/bundles/set?';
  42. const DEL_DELETE_BUNDLE = DEL_PREFIX + '/v1/tags/bundles/delete?';
  43.  
  44. const kHashPropertyBagContractID = "@mozilla.org/hash-property-bag;1";
  45. const kIWritablePropertyBag = Components.interfaces.nsIWritablePropertyBag;
  46. const HashPropertyBag = new Components.Constructor(kHashPropertyBagContractID,
  47.                                                    kIWritablePropertyBag);
  48.  
  49. const kIPropertyBag = Components.interfaces.nsIPropertyBag;
  50. const ROHashPropertyBag = new Components.Constructor( kHashPropertyBagContractID,
  51.                                                       kIPropertyBag );
  52.  
  53. const kMutableArrayContractID = "@mozilla.org/array;1";
  54. const kIMutableArray = Components.interfaces.nsIMutableArray;
  55. const NSArray = new Components.Constructor(kMutableArrayContractID,
  56.                                            kIMutableArray);
  57.  
  58. const kStringContractID = "@mozilla.org/supports-string;1";
  59. const kIString = Components.interfaces.nsISupportsString;
  60. const NSString = new Components.Constructor( kStringContractID, kIString );
  61.  
  62. const SERVICE_NAME = "delicious";
  63. var DEL_UA_STRING = "ffbmext";                             
  64. //const LOGIN_URL = "https://secure.delicious.com/login?jump=http%3A%2F%2Fpreview.delicious.com%2F";
  65. //const LOGIN_URL = "https://qa.delicious.com/login?jump=ub";
  66.  
  67. /*
  68. const LOGIN_URL = "https://staging.delicious.com/login?jump=ub";
  69. const REGISTER_URL = "https://secure.delicious.com/register?src=";
  70. const HOME_URL = "http://delicious.com/";
  71. */
  72.  
  73. const LOGIN_URL = "https://secure.del.icio.us/login?src=";
  74. const REGISTER_URL = "https://secure.del.icio.us/register?src=";
  75. var HOME_URL = "http://del.icio.us/";
  76.  
  77. const BETA_HOME_URL = "http://delicious.com/";
  78. const US_HOME_URL = "http://del.icio.us/";
  79.  
  80.  
  81. const YB_BUNDLE_URI = "bundle:"
  82. const USDOMAIN = ".del.icio.us";
  83. //const COMDOMAIN = "preview.delicious.com";
  84. //const COMDOMAIN = "qa.delicious.com";
  85. const COMDOMAIN = "delicious.com";
  86.  
  87. const DOTCOMDOMAIN = "." + COMDOMAIN;
  88. const nsTimer = "@mozilla.org/timer;1";
  89. const nsITimer = Components.interfaces.nsITimer;
  90.  
  91. var DEL_REQ_TIMEOUT = 30;
  92. var PREFDOMAIN = ".del.icio.us";
  93. var SRVPREFDOMAIN = null;
  94.  
  95. const PREF_SERVER_URL = "http://del.icio.us/prefdomain.xml";
  96. //const PREF_SERVER_URL = "http://localhost/prefdomain.xml";
  97. const PREF_NAME = "prefdomain";
  98.  
  99. var CC = Components.classes;
  100. var CI = Components.interfaces;
  101.  
  102.  
  103.  
  104. /**********************************************************
  105.  * Load yDebug.js
  106.  **********************************************************/
  107. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  108.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  109.         "chrome://ybookmarks/content/yDebug.js" ); 
  110. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  111.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  112.         "chrome://ybookmarks/content/json.js" ); 
  113. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  114.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  115.         "chrome://ybookmarks/content/ybookmarksUtils.js" ); 
  116. ( ( Components.classes["@mozilla.org/moz/jssubscript-loader;1"] ).getService( 
  117.      Components.interfaces.mozIJSSubScriptLoader ) ).loadSubScript( 
  118.         "chrome://ybookmarks/content/providerApis.js" ); 
  119.  
  120. /***********************************************************
  121. class definition
  122. ***********************************************************/
  123.  
  124. //class constructor
  125. function SSRDelicious() {
  126.    this._init();
  127. }
  128.  
  129. // class definition
  130. SSRDelicious.prototype = {
  131.    _userAgent : null,
  132.    _allowImportPolling : true,   
  133.    
  134.    cred: {
  135.       config: {
  136.          domain: '.del.icio.us',
  137.          name: '_user'
  138.       },
  139.       cookie: null,
  140.       user: null,
  141.       _cookieTimer: null,
  142.       
  143.       getUserNameFromCookie: function(cookie) {
  144.         if(cookie && cookie.value) {            
  145.             if(cookie.value.indexOf("%20") != -1) {
  146.                 return cookie.value.split(/%20/)[0];
  147.             } else {
  148.                 return cookie.value.split("+")[0];
  149.             }
  150.         }
  151.         return null;
  152.       },
  153.  
  154.       _storeCookieContents: function( newCookie, message ) {      
  155.          if(newCookie) {
  156.              this.cookie = newCookie;            
  157.             this.user = this.getUserNameFromCookie(newCookie);
  158.             this._userChanged("loggedin");
  159.             try {
  160.                 Components.classes["@mozilla.org/preferences-service;1"]
  161.                   .getService(Components.interfaces.nsIPrefBranch)
  162.                   .setBoolPref("extensions.ybookmarks@yahoo.delicious.silentlogout", false);
  163.             } catch(e) {}
  164.             var d = new Date();
  165.             var timerInterval = newCookie.expires - parseInt(d.getTime()/1000); 
  166.             this._resetCookieTimer(timerInterval);          
  167.          } else if( newCookie == null && !message) {
  168.             this.cookie = this.user = null;
  169.             this._userChanged("loggedout");
  170.             this._resetCookieTimer(null);
  171.          } else if( newCookie == null && message) {
  172.              this.cookie = this.user = null;
  173.             this._userChanged(message);
  174.             this._resetCookieTimer(null);            
  175.          }
  176.       },
  177.       
  178.       _resetCookieTimer : function(interval) {
  179.           if(!this._cookieTimer) {
  180.               this._cookieTimer = CC[nsTimer].createInstance(nsITimer);
  181.           } else {
  182.               this._cookieTimer.cancel();
  183.           }
  184.           if(interval) {
  185.               this._cookieTimer.initWithCallback(this, interval * 1000,
  186.               CI.nsITimer.TYPE_ONE_SHOT);
  187.             yDebug.print("Timer initiated for Cookie", YB_LOG_MESSAGE);  
  188.           }          
  189.       },
  190.       
  191.       notify : function(aTimer) {
  192.           yDebug.print("Got notification for Cookie expiry", YB_LOG_MESSAGE);
  193.           this._storeCookieContents(null, "cookie_expired");    
  194.       },
  195.       
  196.       _userChanged : function(data) {
  197.          Components.classes["@mozilla.org/observer-service;1"]
  198.        .getService(Components.interfaces.nsIObserverService)
  199.            .notifyObservers(null, "ybookmark.userChanged", data);        
  200.       },      
  201.       
  202.       extractCookie: function() {
  203.          var cookieManager = ( Components.classes[ "@mozilla.org/cookiemanager;1" ]
  204.                                            .getService( Components.interfaces.nsICookieManager ) );
  205.          var iter = cookieManager.enumerator; 
  206.          var cookieFound = false;         
  207.          var USCookie = null;
  208.          var USCookieLocal = null;
  209.          while( iter.hasMoreElements() ) { 
  210.             var cookie = iter.getNext(); 
  211.             if( cookie instanceof Components.interfaces.nsICookie ) {
  212.                 if(SRVPREFDOMAIN) { 
  213.                     if(PREFDOMAIN == "fallback") {
  214.                         if(cookie.host == DOTCOMDOMAIN && cookie.name == this.config.name) {
  215.                             yDebug.print( "Reader: found user cookie(fallback)->" + DOTCOMDOMAIN, YB_LOG_MESSAGE );                       
  216.                             this._storeCookieContents( cookie);
  217.                             return;
  218.                         } else if(cookie.host == USDOMAIN && cookie.name == this.config.name) {       
  219.                             yDebug.print( "Reader: found user cookie(fallback)->" + USDOMAIN, YB_LOG_MESSAGE );                       
  220.                             USCookie = cookie;
  221.                             cookieFound = true;                            
  222.                         }
  223.                     } else if(PREFDOMAIN == USDOMAIN) {
  224.                         if(cookie.host == USDOMAIN && cookie.name == this.config.name) {
  225.                            yDebug.print( "Reader: found user cookie->" + USDOMAIN, YB_LOG_MESSAGE );  
  226.                            this._storeCookieContents( cookie);
  227.                            return;
  228.                         }
  229.                     } else if(PREFDOMAIN == DOTCOMDOMAIN) {
  230.                         if(cookie.host == DOTCOMDOMAIN && cookie.name == this.config.name) {
  231.                            yDebug.print( "Reader: found user cookie->" + DOTCOMDOMAIN, YB_LOG_MESSAGE ); 
  232.                            this._storeCookieContents( cookie);
  233.                            return;
  234.                         }
  235.                     } 
  236.                 } else { //Local pref.                
  237.                     if(PREFDOMAIN == USDOMAIN) {
  238.                        if( cookie.host == this.config.domain && cookie.name == this.config.name ) {
  239.                           yDebug.print( "Reader: found user cookie(local pref)->" + this.config.domain, YB_LOG_MESSAGE );
  240.                           USCookieLocal = cookie;                          
  241.                           cookieFound = true;                          
  242.                        }
  243.                     }
  244.                     //look for .com cookie always
  245.                     if((cookie.host == DOTCOMDOMAIN) && cookie.name == this.config.name ) {                        
  246.                         yDebug.print( "Reader: found user cookie(local pref)->" + DOTCOMDOMAIN, YB_LOG_MESSAGE );
  247.                         if(PREFDOMAIN != DOTCOMDOMAIN) {
  248.                             ybookmarksUtils.setPrefDomain(DOTCOMDOMAIN);
  249.                             PREFDOMAIN = DOTCOMDOMAIN;
  250.                         }
  251.                         this._storeCookieContents( cookie );                        
  252.                         return;
  253.                     }
  254.                 }
  255.             }
  256.          }
  257.          if(!SRVPREFDOMAIN && USCookieLocal) {
  258.             this._storeCookieContents( USCookieLocal );
  259.          }
  260.          if(PREFDOMAIN == "fallback" && USCookie) {
  261.             //yDebug.print("Cookie values: name:" + USCookie.name + " host:" + USCookie.host + " value:" + USCookie.value + " expires:" +  USCookie.expires, YB_LOG_MESSAGE);            
  262.             this._storeCookieContents(USCookie);
  263.          }
  264.          //Cookie not found
  265.          if(!cookieFound) {
  266.             yDebug.print( "ssrDelicious::=> No user cookie found.", YB_LOG_MESSAGE );            
  267.          }         
  268.       },
  269.       /*
  270.       * returns cookie if _user cookie is found else return false.
  271.       */
  272.       extractHostCookie: function(hostName) {
  273.          var cookieManager = ( Components.classes[ "@mozilla.org/cookiemanager;1" ]
  274.                                            .getService( Components.interfaces.nsICookieManager ) );
  275.          var iter = cookieManager.enumerator;
  276.          while( iter.hasMoreElements() ) { 
  277.             var cookie = iter.getNext(); 
  278.             if( cookie instanceof Components.interfaces.nsICookie ) {
  279.                 if(cookie.host == hostName && cookie.name == this.config.name) {
  280.                     yDebug.print( "ssrDelicious::extractCookie(): found user cookie:" + hostName, YB_LOG_MESSAGE );                      
  281.                     return cookie;
  282.                 }
  283.             }
  284.          }
  285.          return false;     
  286.       },      
  287.  
  288.       observe: function( subject, topic, data ) {
  289.          try {
  290.              if (topic == "ybookmark.userChanged") {
  291.                 if(data == "triggerSilentLogout") {
  292.                        //this.user = null;
  293.                        try {
  294.                            Components.classes["@mozilla.org/preferences-service;1"]
  295.                           .getService(Components.interfaces.nsIPrefBranch)
  296.                           .setBoolPref("extensions.ybookmarks@yahoo.delicious.silentlogout", true);
  297.                     } catch(e) {}
  298.                     this._userChanged("silentlogout");                    
  299.                 }
  300.                 return;
  301.             } 
  302.             if (topic == "ybookmark.webPrefValue") {
  303.                 if(data) {
  304.                     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  305.                                     .getService(Components.interfaces.nsIPrefBranch);
  306.                     var debug = false;
  307.                     var testWebPref = null;
  308.                     try {
  309.                       debug = prefs.getBoolPref("extensions.ybookmarks@yahoo.debug");
  310.                       if(debug) {
  311.                         var testWebPref = prefs.getCharPref("extensions.ybookmarks@yahoo.webpref.testing");
  312.                         if(testWebPref) {
  313.                             if(testWebPref == ".del.icio.us" || testWebPref == ".delicious.com" || testWebPref == "fallback") { 
  314.                                 yDebug.print( "TESTING WITH WEBPREF:" + testWebPref,
  315.                                  YB_LOG_MESSAGE );
  316.                             } else {                            
  317.                                 yDebug.print( "INCORRECT VALUE FOR TESTING,WEBPREF:" + testWebPref,
  318.                                  YB_LOG_MESSAGE );
  319.                                 testWebPref = null; 
  320.                             }
  321.                         }
  322.                       }
  323.                     } catch (e) {}
  324.                     if(testWebPref) {
  325.                         PREFDOMAIN = SRVPREFDOMAIN = testWebPref;
  326.                         this.extractCookie();
  327.                     } else if(data != "server-pref-failed") {             
  328.                         PREFDOMAIN = SRVPREFDOMAIN = data;
  329.                         this.extractCookie();
  330.                     }
  331.                     yDebug.print( "Got web preference:" + PREFDOMAIN,
  332.                              YB_LOG_MESSAGE );
  333.                 }
  334.                 return;
  335.             }
  336.             
  337.             if (data == "cleared") {
  338.                yDebug.print( "RD: The entire cookie store has been cleared",
  339.                              YB_LOG_MESSAGE );
  340.  
  341.                this._storeCookieContents();
  342.                return;
  343.             }
  344.                         
  345.             try {
  346.             subject.QueryInterface( Components.interfaces.nsICookie );
  347.             } catch(e) {
  348.                 yDebug.print( "ssrdelicious.cred.observe:QI failed", YB_LOG_MESSAGE ); 
  349.                 return;
  350.             }            
  351.             
  352.             /**
  353.              * check for PREFDOMAIN and add new logic
  354.              */
  355.             if(SRVPREFDOMAIN) {  
  356.                 if(PREFDOMAIN == "fallback") {
  357.                     if(subject.host == DOTCOMDOMAIN && subject.name == "_user") {
  358.                         if(data == "added" || data == "changed") {
  359.                             this._storeCookieContents( subject );
  360.                         } else if( data == "deleted" ) {                            
  361.                             //Find and use .us cookie if any.
  362.                             var USCookie = this.extractHostCookie(USDOMAIN);
  363.                             if(USCookie) {
  364.                                 this._storeCookieContents(USCookie);//Login with USCookie.
  365.                             } else {
  366.                                 this._storeCookieContents();//Logout
  367.                             }
  368.                         }
  369.                     } else if( subject.host == USDOMAIN && subject.name == "_user" ) {                                            
  370.                         if(this.user && (this.cookie.host ==  DOTCOMDOMAIN)) {
  371.                             yDebug.print("Reader: Ignoring .us cookie change as .com user is logged in", YB_LOG_MESSAGE);
  372.                             return;
  373.                         }
  374.                         if(data == "added" || data == "changed") {
  375.                             this._storeCookieContents( subject );
  376.                         } else if( data == "deleted" ) {
  377.                             this._storeCookieContents();
  378.                         }
  379.                     }
  380.                 } else if(PREFDOMAIN == USDOMAIN) {
  381.                     if( subject.host == USDOMAIN && subject.name == "_user" ) {
  382.                         if(data == "added" || data == "changed") {
  383.                             this._storeCookieContents( subject );
  384.                         } else if( data == "deleted" ) {
  385.                             this._storeCookieContents();
  386.                         }
  387.                     }
  388.                 } else if(PREFDOMAIN == DOTCOMDOMAIN) {
  389.                     if(subject.host == DOTCOMDOMAIN && subject.name == "_user") {
  390.                         if(data == "added" || data == "changed") {
  391.                             this._storeCookieContents( subject );
  392.                         } else if( data == "deleted" ) {
  393.                             this._storeCookieContents();
  394.                         }
  395.                     }
  396.                 }
  397.             } else { //use local logic
  398.                 if(PREFDOMAIN == USDOMAIN) {
  399.                     if( subject.host == USDOMAIN && subject.name == "_user" ) {
  400.                        yDebug.print( "Reader: " + data 
  401.                                      + " user cookie", YB_LOG_MESSAGE );
  402.  
  403.                        if( data == "added" || data == "changed") {
  404.                           this._storeCookieContents( subject );
  405.                        }
  406.                        else if( data == "deleted" ) {
  407.                           this._storeCookieContents();
  408.                        }
  409.                     }
  410.                 }
  411.                 
  412.                 //always use .com cookie
  413.                 if( (subject.host == DOTCOMDOMAIN) && subject.name == "_user" ) {
  414.                    yDebug.print( "Reader: " + data 
  415.                                  + " user cookie", YB_LOG_MESSAGE );
  416.                    if( data == "added" || data == "changed") {
  417.                       if(PREFDOMAIN != DOTCOMDOMAIN) {
  418.                           ybookmarksUtils.setPrefDomain(DOTCOMDOMAIN);
  419.                           PREFDOMAIN = DOTCOMDOMAIN;
  420.                       }
  421.                       this._storeCookieContents( subject );
  422.                    }
  423.                    else if( data == "deleted" ) {
  424.                       this._storeCookieContents();
  425.                    }
  426.                 }
  427.             }
  428.          } 
  429.          catch ( e ) {
  430.             yDebug.print( "exception in ssrdelicious.cred.observe: " + e, YB_LOG_MESSAGE );
  431.          }
  432.       }
  433.    },
  434.  
  435.    _init: function() {
  436.       yDebug.print("DEL._INIT loading");
  437.       var pref = Components.classes["@mozilla.org/preferences-service;1"]
  438.                       .getService(Components.interfaces.nsIPrefBranch);
  439.                       
  440.       //Get local pref
  441.       PREFDOMAIN = ybookmarksUtils.getPrefDomain();      
  442.       yDebug.print("ssrDelicious::_Init()::=> Initial PREFDOMAIN:"+PREFDOMAIN, YB_LOG_MESSAGE);
  443.       this.cred.extractCookie();
  444.       
  445.       var observService = Components.classes[ "@mozilla.org/observer-service;1" ].
  446.       getService( Components.interfaces.nsIObserverService );
  447.       observService.addObserver( this.cred, "cookie-changed", false );        
  448.         observService.addObserver( this.cred, "ybookmark.userChanged", false );//add observer to look for silent logout
  449.         observService.addObserver( this.cred, "ybookmark.webPrefValue", false );//add observer to look for silent logout
  450.         //Get Server pref.
  451.         ybookmarksUtils.getServerPref(PREF_SERVER_URL, PREF_NAME);
  452.         
  453.       var mediator =
  454.          (Components.classes["@mozilla.org/appshell/window-mediator;1"].
  455.           getService(Components.interfaces.nsIWindowMediator));
  456.       
  457.       var assClass =
  458.          Components.classes["@mozilla.org/appshell/appShellService;1"];
  459.       var ass = assClass.getService(Components.interfaces.nsIAppShellService);
  460.       gHiddenWin = ass.hiddenDOMWindow;
  461.  
  462.       this.btoaCookie = gHiddenWin.btoa('cookie:cookie');
  463.  
  464.       var authMgrClass =
  465.          Components.classes["@mozilla.org/network/http-auth-manager;1"];
  466.       this.authMgr = authMgrClass.getService(Components.interfaces.nsIHttpAuthManager);
  467.  
  468.       var bundleService = 
  469.             Components.classes[ "@mozilla.org/intl/stringbundle;1" ].getService( 
  470.                 Components.interfaces.nsIStringBundleService );
  471.       var bundle = 
  472.                bundleService.createBundle( "chrome://ybookmarks/locale/ybookmarks.properties" );
  473.       var version = bundle.GetStringFromName( "extensions.ybookmarks.versionNum" );
  474.       DEL_UA_STRING += version; 
  475.  
  476.       
  477.       try {
  478.       DEL_REQ_TIMEOUT = pref.getIntPref(
  479.               "extensions.ybookmarks@yahoo.bookmark.request.timeout");
  480.       } catch (e) {}     
  481.               
  482.       yDebug.print("Request timeout period:" + DEL_REQ_TIMEOUT, YB_LOG_MESSAGE);
  483.  
  484.       yDebug.print("DEL._INIT loaded");
  485.    },
  486.  
  487.    /**
  488.     * Obtains the date and time of the last update.
  489.     * @param cb the callback handler. The onload method should receive an array
  490.     * with a property bag with the property "time", indicating the date and time
  491.     * of the last update.
  492.     */
  493.    lastUpdate: function(cb) {
  494.       yDebug.print("DEL LASTUPDATE", YB_LOG_MESSAGE);
  495.  
  496.       var onload = function(event) {
  497.          yDebug.print("LOAD lastUpdate", YB_LOG_MESSAGE);
  498.          
  499.          if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  500.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  501.             cb.onerror(event);
  502.             return;
  503.          }
  504.       
  505.          var doc = event.target.responseXML;
  506.          //yDebug.print(event.target.responseText);
  507.          var nodes = doc.getElementsByTagName("update");
  508.       
  509.          yDebug.print("del nodelen:" + nodes.length, YB_LOG_MESSAGE);
  510.          
  511.          var timeAttr = nodes[0].getAttribute("time");
  512.          var inboxnew = nodes[0].getAttribute("inboxnew");
  513.          
  514.          var updateTime = ssrDeliciousHelper._getTimeFromString(timeAttr);
  515.  
  516.          var result = new NSArray();
  517.          var data = new HashPropertyBag();
  518.          data.setProperty("time", updateTime);
  519.          data.setProperty("inboxnew", inboxnew);
  520.          result.appendElement(data, false);
  521.  
  522.          cb.onload(result);
  523.       };
  524.     
  525.       var onerror = function(event) {
  526.           yDebug.print("Failed lastUpdate", YB_LOG_MESSAGE);
  527.          cb.onerror(event);
  528.       };
  529.       
  530.       /*  
  531.       if (this.lastUpdateReq != null) {
  532.            yDebug.print("DEL LASTUPDATE 2", YB_LOG_MESSAGE);
  533.          try {
  534.             this.lastUpdateReq.abort();
  535.          } catch (e) {
  536.             yDebug.print("lastUpdateReq.abort failed: " + e, YB_LOG_MESSAGE);
  537.          }
  538.       }*/
  539.  
  540.       if(this.cred.cookie != null) {
  541.           this.lastUpdateReq = this._post(DEL_UPDATE_URL, onload, onerror);
  542.       }
  543.    },
  544.  
  545.    /**
  546.     * Performs a POST operation.
  547.     * @param url the URL where the POST is sent.
  548.     * @param onload load handler.
  549.     * @param onerror error handler.
  550.     * @param async indicates whether the response should be received
  551.     * asynchronously.
  552.     */
  553.    _post: function(url, onload, onerror, async) {
  554.       var str = "";
  555.  
  556.       if (this.cred.cookie != null) {
  557.         str = '_user=' + encodeURIComponent( this.cred.cookie.value );
  558.       }   
  559.     
  560.       return this._postWithContent(
  561.          url, "application/x-www-form-urlencoded", str, onload,
  562.          onerror, async);
  563.    },
  564.    
  565.    /**
  566.     * Performs a POST operation that sends content in its body.
  567.     * @param url the URL where the POST is sent.
  568.     * @param contentType the type of content being sent.
  569.     * @param content the content being sent.
  570.     * @param onload load handler.
  571.     * @param onerror error handler.
  572.     * @param async indicates whether the response should be received
  573.     * asynchronously.
  574.     */
  575.    _postWithContent: function(url, contentType, content, onload, onerror, async) {
  576.       var req = 
  577.          Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
  578.             createInstance();
  579.           
  580.       if(async == null) {
  581.          async = true;
  582.       }
  583.       
  584.     
  585.      /* add the src parameter. */
  586.       if (url.indexOf("?") > 0) {
  587.          if (url[url.length - 1] != "?") {
  588.             url += "&";
  589.          }   
  590.       } else {
  591.          url += "?";
  592.       }      
  593.       url += "src=" + DEL_UA_STRING;
  594.       
  595.       var onLoad = function(event) {
  596.         yDebug.print("SSrDelicious: postwithContent function,onLoad", YB_LOG_MESSAGE);  
  597.         if (event.target.status == "200") {
  598.             Components.classes["@mozilla.org/observer-service;1"]
  599.                  .getService(Components.interfaces.nsIObserverService)
  600.                .notifyObservers(null, "ybookmark.internalServerStatus", "200");            
  601.         }
  602.         if (event.target.status == 500 || event.target.status == 503) {          
  603.                yDebug.print("SSrDelicious: postwithContent function,onLoad:" + event.target.status, YB_LOG_MESSAGE);
  604.                Components.classes["@mozilla.org/observer-service;1"]
  605.                  .getService(Components.interfaces.nsIObserverService)
  606.                .notifyObservers(null, "ybookmark.internalServerStatus", "500");        
  607.            }
  608.         onload(event);  
  609.       }     
  610.       
  611.       var onError = function(event) {
  612.          yDebug.print("SSrDelicious: postwithContent function,onError:" + event.target.status, YB_LOG_MESSAGE);
  613.          onerror(event);
  614.       };     
  615.  
  616.       req.open('POST', url, async); 
  617.       req.onload = onLoad;
  618.       //req.onerror = onError;
  619.       req.onerror = onerror;
  620.  
  621.       // abort the request if we don't reach state 2 LOADED in 30 seconds
  622.       // things like import may take a while to reach state 4 so just handle
  623.       // not reaching state 2 in 30
  624.       var reqTimeoutId =
  625.          gHiddenWin.setTimeout(function(req) {
  626.                                  yDebug.print("REQTIMEOUT:" + url, YB_LOG_MESSAGE);
  627.                                  req.abort();
  628.                                  var event = {};
  629.                                  onerror(event); 
  630.                                },
  631.                                DEL_REQ_TIMEOUT * 1000,
  632.                                req);
  633.  
  634.       var end = url.indexOf('?');
  635.       var path = url.substring(DEL_PREFIX.length, end);
  636.  
  637.       var reader = this;
  638.       req.onreadystatechange = function(event) {
  639.          if (req.readyState == 2) {
  640.             gHiddenWin.clearTimeout(reqTimeoutId);
  641.          } else if (req.readyState == 4) {
  642.             //void setAuthIdentity ( ACString scheme , ACString host , PRInt32 port , ACString authType , ACString realm , ACString path , AString userDomain , AString userName , AString userPassword )
  643.             //scheme: the URL scheme (e.g., "http"). NOTE: for proxy authentication, this should be "http" (this includes authentication for SSL tunneling). 
  644.             //host: the host of the server issuing a challenge (ASCII only). 
  645.             //port: the port of the server issuing a challenge. 
  646.             //authType: optional string identifying auth type used (e.g., "basic") 
  647.             //realm: optional string identifying auth realm. 
  648.             //path: optional string identifying auth path. empty for proxy auth. 
  649.             //userDomain: optional string containing user domain. 
  650.             //userName: optional string containing user name. 
  651.             //userPassword: optional string containing user password.
  652.             var protocol = url.match(/^http|https/);
  653.             if (protocol != null) {
  654.                reader.authMgr.setAuthIdentity(
  655.                   protocol,
  656.                   'api.del.icio.us',
  657.                   443,
  658.                   'basic',
  659.                   'del.icio.us API',
  660.                   path,
  661.                   '',
  662.                   '',
  663.                   ''
  664.                );
  665.             }
  666.          }
  667.       };
  668.  
  669.       // There seems to be some issue where delicious will return auth
  670.       // failure even if the cookie is right - bug 861913.
  671.       // override nsIAuthPrompt
  672.       req.channel.notificationCallbacks = {
  673.          prompt: function(dialogTitle , text , passwordRealm , savePassword ,
  674.                           defaultText , result ) {
  675.             yDebug.print("User got prompt", YB_LOG_MESSAGE);
  676.             return true;
  677.          },
  678.  
  679.          promptPassword: function(dialogTitle , text , passwordRealm ,
  680.                                   savePassword , pwd ) {
  681.             yDebug.print("User got promptPassword", YB_LOG_MESSAGE);
  682.             return true;
  683.          },
  684.  
  685.          promptUsernameAndPassword: function(dialogTitle , text ,
  686.                           passwordRealm , savePassword , user , pwd ) {
  687.             yDebug.print("User got promptUsernameAndPassword", YB_LOG_MESSAGE);
  688.             return true;
  689.          },
  690.  
  691.          QueryInterface: function(aIID)
  692.          {
  693.             if (!aIID.equals(Components.interfaces.nsIAuthPrompt) &&
  694.                 !aIID.equals(Components.interfaces.nsIInterfaceRequestor) &&
  695.                 !aIID.equals(Components.interfaces.nsISupports))
  696.                throw Components.results.NS_ERROR_NO_INTERFACE;
  697.             return this;
  698.          },
  699.  
  700.          getInterface: function(iid) {
  701.             if (iid.equals(Components.interfaces.nsIAuthPrompt))
  702.                return this;
  703.  
  704.             Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
  705.             return null;
  706.          }
  707.       };
  708.       
  709.       protocol = url.match(/^http|https/);
  710.       if (protocol != null) {
  711.          this.authMgr.setAuthIdentity(
  712.             protocol,
  713.             'api.del.icio.us',
  714.             443,
  715.             'basic',
  716.             'del.icio.us API',
  717.             path,
  718.             '',
  719.             'cookie',
  720.             'cookie'
  721.          );
  722.          req.setRequestHeader('Authorization', 'Basic '+ this.btoaCookie);
  723.       }
  724.       
  725.       req.setRequestHeader("Content-Type", contentType);
  726.       req.setRequestHeader("User-Agent", this._getUserAgentString());      
  727.  
  728.       yDebug.print("POSTING to \<" + url 
  729.                    + "\> with useragent \<" + this._getUserAgentString()
  730.                    + "\>",
  731.                    YB_DEBUG_MESSAGE);
  732.  
  733.       req.send(content);
  734.  
  735.       return req;
  736.    },
  737.    
  738.    /**
  739.     * Obtains the User-Agent string to be sent to the server.
  740.     * @ return the User-Agent string to be sent to the server.
  741.     */
  742.    _getUserAgentString: function() {
  743.       if (!this._userAgent) {
  744.          var mediator =
  745.             Components.classes["@mozilla.org/appshell/window-mediator;1"].
  746.                getService(Components.interfaces.nsIWindowMediator);
  747.       
  748.          var win = mediator.getMostRecentWindow(null);
  749.          
  750.          this._userAgent = win.navigator.userAgent + ";" + DEL_UA_STRING;
  751.       }
  752.       
  753.       return this._userAgent;
  754.    },
  755.  
  756.    /**
  757.     * Returns the information of all bookmarks within the given range. Returns
  758.     * an empty list if there are no bookmarks in the range.
  759.     * @param start the (zero-based) position of the first bookmark to obtain.
  760.     * @param count the amount of bookmarks to obtain.
  761.     * @param cb the callback handler. The onload method should receive the
  762.     * collection of obtained bookmarks.
  763.     */
  764.    allBookmarks: function(start, count, cb) {
  765.       yDebug.print("DEL ALLBOOKMARKS");
  766.       
  767.       var queryString = DEL_ALL_URL + "results=" + count + "&start=" + start + "&meta=1";
  768.       
  769.       var onerror = function(event) {
  770.          cb.onerror(event);
  771.       };
  772.  
  773.       var onload = function(event) {
  774.  
  775.         // for all bookmarks, set the first element in the result set to the total
  776.         // number of items if all items were downloaded at once.
  777.         var posts = ssrDeliciousHelper._loadBookmarks( event, true );
  778.         if ( posts ) {
  779.           cb.onload( posts );
  780.         }
  781.         else {
  782.           cb.onerror(event);
  783.         }  
  784.       };
  785.  
  786.       this._post(queryString, onload, onerror);
  787.    },
  788.    
  789.    /**
  790.     * Obtains the bookamrks corresponding to the provided URL hashes.
  791.     * @param hashes array of URL hashes that dictate which bookmarks to
  792.     * download.
  793.     * @param cb the callback handler. The onload method should receive the
  794.     * collection of obtained bookmarks.
  795.     */
  796.    getBookmarksForHashes: function(hashes, cb) {
  797.       yDebug.print("DEL GET BOOKMARKS FOR HASHES");
  798.       
  799.       var queryString = DEL_GETBOOKMARKS_URL + "hashes=";
  800.       
  801.       for (var i = 0; i < hashes.length; i++) {
  802.          queryString += 
  803.            hashes.queryElementAt(i, Components.interfaces.nsISupportsString);
  804.         
  805.         
  806.          if (i != (hashes.length - 1)) {
  807.            queryString += "+";
  808.          }
  809.       }
  810.       
  811.       queryString += "&meta=1";
  812.       
  813.       yDebug.print("::::" + queryString, YB_LOG_MESSAGE);
  814.       
  815.       var onerror = function(event) {
  816.          cb.onerror(event);
  817.       };
  818.  
  819.       var onload = function(event) {
  820.  
  821.         yDebug.print(event.target.responseText);
  822.         var posts = ssrDeliciousHelper._loadBookmarks( event );
  823.         if ( posts )
  824.           cb.onload( posts );
  825.         else
  826.           return;
  827.       };
  828.       
  829.       this._post(queryString, onload, onerror);
  830.    },
  831.  
  832.    /**
  833.      * Obtains the bookamrks corresponding to the provided URL.
  834.      * @param URL that dictate which bookmark to download
  835.      * @param cb the callback handler. The onload method should receive the
  836.      * obtained bookmarks.
  837.      */
  838.    getBookmarkForURL : function(url, cb) {
  839.    
  840.      yDebug.print("GRT BOOKMARK FOR URL :" + url );
  841.      var queryString = DEL_GETBOOKMARKS_URL + "url=" + encodeURIComponent(url) + "&meta=1";
  842.           
  843.      var onerror = function(event) {
  844.        cb.onerror(event);
  845.      };
  846.      
  847.      var onload = function(event) {
  848.        
  849.        yDebug.print(event.target.responseText);
  850.        var posts = ssrDeliciousHelper._loadBookmarks( event );       
  851.        if ( posts )
  852.          cb.onload( posts );
  853.        else
  854.          cb.onerror( event );
  855.      };
  856.      
  857.      this._post(queryString, onload, onerror);
  858.    },
  859.    
  860.    /**
  861.     * Obtains the hashes for the URL and extra information on all the user's bookmarks.
  862.     * @param cb the callback handler. The onload method should receive a
  863.     * collection of property bags that contain booth hashes (urlHash and bookmarkHash).
  864.     */
  865.    getBookmarkHashes: function(cb) {
  866.       yDebug.print("DEL BOOKMARK HASHES");
  867.       
  868.       var queryString = DEL_ALL_URL + "&hashes";
  869.       
  870.       var onerror = function(event) {
  871.          cb.onerror(event);
  872.       };
  873.  
  874.       var onload = function(event) {
  875.          yDebug.print("LOAD getBookmarkHashes");
  876.          
  877.          if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  878.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  879.             cb.onerror(event);
  880.             return;
  881.          }
  882.          
  883.          //yDebug.print(event.target.responseText);
  884.       
  885.         var result = new NSArray();
  886.          //var nodes = YBJSON.parse(event.target.responseText );
  887.         var doc = event.target.responseXML;
  888.  
  889.         if (doc.getElementsByTagName('posts').length != 1) {
  890.            yDebug.print("Failed: Invalid \'all\' result", YB_LOG_MESSAGE);
  891.            yDebug.print(event.target.responseText, YB_LOG_MESSAGE);
  892.            yDebug.print(event.target.responseXML, YB_LOG_MESSAGE);
  893.            cb.onerror(event);
  894.            return;
  895.         }
  896.  
  897.         var nodes = doc.getElementsByTagName('post');
  898.  
  899.          for (var i = 0; i < nodes.length; i++) {
  900.             var data = new HashPropertyBag();
  901.             var node = nodes[i];
  902.             var hash = node.getAttribute("url");
  903.             var metahash = node.getAttribute("meta");
  904.  
  905.             data.setProperty("hash", hash);
  906.             data.setProperty("metahash", metahash);
  907.             result.appendElement(data, false);
  908.          }
  909.  
  910.          cb.onload(result);
  911.       };
  912.  
  913.       this._post(queryString, onload, onerror);
  914.    },
  915.  
  916.    /**
  917.     * Imports a set of bookmarks to the remote list.
  918.     * @param bookmarks this is a string that corresponds to an HTML-formatted
  919.     * document holding the bookmarks. Its format should be the same used by
  920.     * applications like Firefox and IE.
  921.     * @param userTags an array of tags set by the user. The tag "imported" is
  922.     * automatically added if the list is empty.
  923.     * @param addPopularTags true if the currently popular tags should be added
  924.     * to the bookmarks.
  925.     * @param overwrite true if current bookmarks should be overwritten with
  926.     * imported bookmarks..
  927.     * @param cb the callback handler. The onload method should receive an array
  928.     * with a property bag with the property "status", which can have any of the
  929.     * following values: "accepted" or "busy".
  930.     */
  931.    importBookmarks: function (bookmarks, userTags, addPopularTags, overwrite, email, priv,
  932.                               cb) {
  933.       yDebug.print("DEL IMPORT");
  934.       
  935.       var queryString =
  936.         DEL_IMPORT_URL + "&clobber_existing=" + (overwrite ? 1 : 0) +
  937.         "&add_popular=" + (addPopularTags ? 1 : 0);
  938.       
  939.       if (userTags.length > 0) {
  940.          queryString += "&user_add_tags=";
  941.       
  942.          for (var i = 0; i < userTags.length; i++) {
  943.            queryString += 
  944.               userTags.queryElementAt(i,
  945.                                       Components.interfaces.nsISupportsString);
  946.         
  947.            if (i != (userTags.length - 1)) {
  948.              queryString += " ";
  949.            }
  950.          }
  951.       }
  952.       queryString+= "&email_wait=" + (email ? 1:0) + "&is_private="+(priv ? 1:0)
  953.       
  954.       var onerror = function(event) {
  955.          yDebug.print("IMPORT ERROR");
  956.          cb.onerror(event);
  957.       };
  958.  
  959.       var onload = function(event) {
  960.          yDebug.print("IMPORT ONLOAD");
  961.          
  962.          if (!ssrDeliciousHelper._isValidResponse(event, true)) {
  963.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  964.             return;
  965.          }
  966.          
  967.          yDebug.print(event.target.responseText);
  968.       
  969.          var result = new NSArray();
  970.          var response = YBJSON.parse(event.target.responseText);
  971.          var data = new HashPropertyBag();
  972.        
  973.          data.setProperty("status", response["status"]);
  974.          result.appendElement(data, false);
  975.          yDebug.print("importbookmarks: status - "+response["status"], YB_LOG_MESSAGE);
  976.  
  977.          cb.onload(result);
  978.       };
  979.  
  980.       if (this.cred.cookie == null) {
  981.          yDebug.print("COOKIE MISSING - USER NOT LOGGED IN");
  982.  
  983.          var result = new NSArray();
  984.          var data = new HashPropertyBag();
  985.          //what's the error content?
  986.          //data.setProperty("status", response["status"]);
  987.          result.appendElement(data, false);
  988.  
  989.          cb.onerror(event);
  990.          return;
  991.       }
  992.       yDebug.print("importBookmarks queryString: " + queryString);
  993.       //Note that bookmarks string is already encoded.                      
  994.       this._postWithContent(queryString,
  995.                             "application/x-www-form-urlencoded",
  996.                             '_user=' + encodeURIComponent( this.cred.cookie.value )
  997.                             + '&bookmark_data=' 
  998.                             + bookmarks,
  999.                             onload,
  1000.                             onerror);      
  1001.    },
  1002.    
  1003.    /**
  1004.     * Obtains the status of an import operation.
  1005.     * @param cb the callback handler. The onload method should receive an array
  1006.     * with a property bag with the property "status", which can have any of the
  1007.     * following values: "complete", "importing" or "failed".
  1008.     */
  1009.    getImportStatus: function (cb) {
  1010.       yDebug.print("DEL IMPORT STATUS");
  1011.       
  1012.       if (!this._allowImportPolling) {
  1013.         yDebug.print("getImportStatus(): _allowImportPolling is false");
  1014.         return;
  1015.       }
  1016.       var onerror = function(event) {
  1017.          cb.onerror(event);
  1018.       };
  1019.  
  1020.       var onload = function(event) {
  1021.          yDebug.print("LOAD getImportStatus");
  1022.          
  1023.          if (!ssrDeliciousHelper._isValidResponse(event, true)) {
  1024.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  1025.             cb.onerror(event);
  1026.             return;
  1027.          }
  1028.          
  1029.          //yDebug.print(event.target.responseText);
  1030.       
  1031.          var result = new NSArray();
  1032.          var response = YBJSON.parse(event.target.responseText);
  1033.          var data = new HashPropertyBag();
  1034.        
  1035.          data.setProperty("status", response["status"]);
  1036.          result.appendElement(data, false);
  1037.  
  1038.          cb.onload(result);
  1039.       };
  1040.  
  1041.       this._post(DEL_IMPORT_STATUS_URL, onload, onerror);
  1042.    },
  1043.    
  1044.    allowImportPolling: function() {
  1045.       yDebug.print("_allowImportPolling set to true"); 
  1046.       this._allowImportPolling = true;
  1047.    },
  1048.    
  1049.    disallowImportPolling: function() {
  1050.       yDebug.print("_allowImportPolling set to false");
  1051.       this._allowImportPolling = false;
  1052.    },
  1053.    
  1054.    
  1055.    addBookmark: function( newPost, cb ) {
  1056.  
  1057.       var resultArray = new NSArray();  //for onerror      
  1058.       try {
  1059.          var url = newPost.getProperty( "url" );
  1060.          var desc = newPost.getProperty( "title" );
  1061.       }
  1062.       catch( e ) {
  1063.         cb.onerror(resultArray);
  1064.  
  1065.         return;
  1066.       }
  1067.       if( url.length == 0 || desc.length == 0 ) {
  1068.         cb.onerror(resultArray);
  1069.         return;
  1070.       }
  1071.       
  1072.       try {
  1073.         var notes = newPost.getProperty( "notes" );
  1074.       } catch( e ) { }
  1075.  
  1076.       try {
  1077.          var tags = newPost.getProperty( "tags" );
  1078.          yDebug.print ( "Tags found: " + typeof tags );
  1079.       } catch( e ) { }
  1080.  
  1081.       try {
  1082.         var shortcut = newPost.getProperty( "shortcut" );
  1083.         if ( shortcut != "" ) {
  1084.           if ( tags )
  1085.             tags = tags + " " + "shortcut:" + shortcut;
  1086.           else
  1087.             tags = "shortcut:" + shortcut;
  1088.         }
  1089.       } catch ( e ) { }
  1090.       
  1091.       try {
  1092.          var shared = newPost.getProperty( "shared" );
  1093.       } catch( e ) { }
  1094.       
  1095.       var queryString = DEL_ADDBOOKMARK_URL + 
  1096.          "url=" + encodeURIComponent( url ) + 
  1097.          "&description=" + encodeURIComponent( desc );
  1098.  
  1099.       // Delicious does not support microsummaries. Instead we push microsummaries details in the
  1100.       // notes section.
  1101.       var notesPrefix;
  1102.       try {
  1103.         var microsummary = newPost.getProperty( "microsummary" );
  1104.         yDebug.print ( "Parsing microsummary" );
  1105.         // If notes has microsummary, remove it first
  1106.         if ( notes && notes.match( /\[microsummary:/ ) ) {
  1107.           notes = notes.replace( /\[microsummary:[^\]]+\]\s*/, "" );
  1108.         }
  1109.         notesPrefix = "[microsummary: " + microsummary + "]\n";
  1110.         if ( notes )
  1111.           notes = notesPrefix + notes;
  1112.         else
  1113.           notes = notesPrefix;
  1114.       } catch ( e ) {
  1115.         if ( notes && notes.match( /\[microsummary:/ ) ) {
  1116.           notes = notes.replace( /\[microsummary:[^\]]+\]\s*/, "" );
  1117.         }
  1118.       }
  1119.  
  1120.       // We push postdata details for keyword in the notes section.      
  1121.       try {
  1122.         var postData = newPost.getProperty( "postData" );
  1123.         if (postData.length) {
  1124.           
  1125.           yDebug.print ( "PostData:" + postData);
  1126.           // If notes has postdata, remove it first
  1127.           if ( notes && notes.match( /\[postdata:/ ) ) {
  1128.             notes = notes.replace( /\[postdata:[^\]]+\]\s*/, "" );
  1129.           }
  1130.  
  1131.           notesPrefix = "[postdata:" + postData + "]\n";
  1132.           if ( notes )
  1133.             notes = notesPrefix + notes;
  1134.           else
  1135.             notes = notesPrefix;
  1136.           
  1137.           yDebug.print ( "Final postdata: " + notes);   
  1138.         }
  1139.         
  1140.       } catch(e) { }
  1141.  
  1142.       if( ( notes != null ) && ( notes.length > 0 ) ) {
  1143.          queryString += "&extended=" + encodeURIComponent( notes );
  1144.       }
  1145.  
  1146.       if (shared == "false") {      
  1147.          queryString += "&shared=no";
  1148.       }
  1149.  
  1150.       if ( tags ) {
  1151.         queryString += "&tags=" + encodeURIComponent( tags );
  1152.       }
  1153.       
  1154.       var onload = function( event ) {
  1155.         
  1156.         var retval = ssrDeliciousHelper._parseResponseFromDelicious( event );
  1157.         if ( retval.result ) {
  1158.           cb.onload( retval.data );
  1159.         }  
  1160.         else {
  1161.           cb.onerror( retval.data );
  1162.           yDebug.print( "Error Response (Add bm): " + event.target.status + ", " + event.target.statusText , YB_LOG_MESSAGE );            
  1163.         }
  1164.       };
  1165.  
  1166.       var onerror = function( event ) {
  1167.         cb.onerror( (ssrDeliciousHelper._parseErrorResponseFromDelicious(event)).data );
  1168.         try {
  1169.           yDebug.print( "Error Response (Add bm): " + event.target.status + ", " + event.target.statusText , YB_LOG_MESSAGE );
  1170.         } catch(e) { }
  1171.       };
  1172.       
  1173.       yDebug.print( "Going to POST: " + queryString, YB_LOG_MESSAGE );      
  1174.       this._post( queryString, onload, onerror);
  1175.    },
  1176.    
  1177.    
  1178.    editBookmark : function( editPost, cb ) {
  1179.      this.addBookmark (editPost, cb);   
  1180.    },
  1181.    
  1182.   getFeedData : function( feedURL, cb){
  1183.         
  1184.       var onload = function( event ) { 
  1185.         try { 
  1186.               yDebug.print("getfeedata::onload",YB_LOG_MESSAGE);                        
  1187.             if(event.target.responseXML) {
  1188.                 var arr = new NSArray();
  1189.                 arr.appendElement(event.target.responseXML, false);
  1190.                 cb.onload( arr );
  1191.             }
  1192.         } catch(e) {
  1193.             yDebug.print("Exception in getFeedData::onload() => "+e, YB_LOG_MESSAGE);
  1194.             cb.onerror( e );
  1195.         }
  1196.       };
  1197.  
  1198.       var onerror = function( event ) {        
  1199.         cb.onerror( event );
  1200.       };
  1201.       
  1202.       this._post( feedURL, onload, onerror );
  1203.   },
  1204.    
  1205.    deleteBookmark : function( url, cb ) {
  1206.    
  1207.       var queryString = DEL_DELETEBOOKMARK_URL + 
  1208.          "url=" + encodeURIComponent( url );
  1209.  
  1210.       var onload = function( event ) {
  1211.         var retval = ssrDeliciousHelper._parseResponseFromDelicious( event );
  1212.         if ( retval.result ) {
  1213.           cb.onload( retval.data );
  1214.         }
  1215.         else {
  1216.           cb.onerror( retval.data );
  1217.           yDebug.print( "Error Response (del bm): " + event.target.status + ", " + event.target.statusText, YB_LOG_MESSAGE );
  1218.         }
  1219.       };
  1220.  
  1221.       var onerror = function( event ) {
  1222.         cb.onerror( (ssrDeliciousHelper._parseErrorResponseFromDelicious(event)).data );
  1223.         try {
  1224.           yDebug.print( "Error Response (del bm): " + event.target.status + ", " + event.target.statusText , YB_LOG_MESSAGE );
  1225.         } catch(e){}  
  1226.       };
  1227.       
  1228.       yDebug.print( "Going to POST: " + queryString, YB_LOG_MESSAGE );
  1229.       this._post( queryString, onload, onerror);
  1230.    },
  1231.      
  1232.    getSuggestedTags: function( url, cb ) {
  1233.       yDebug.print( "DEL.GET_SUGGESTED_TAGS" );
  1234.  
  1235.       var queryString = DEL_SUGGEST_URL + encodeURIComponent( url );
  1236.       
  1237.       var onload = function( event ) {
  1238.          if (!ssrDeliciousHelper._isValidResponse(event, true)) {
  1239.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  1240.             return;
  1241.          }
  1242.       
  1243.          var data = YBJSON.parse(event.target.responseText);
  1244.          var tagArr = new NSArray();
  1245.          var tags = new HashPropertyBag();
  1246.          var prop, type, array;
  1247.          for( prop in data ) {
  1248.             type = new NSString();
  1249.             type.data = prop;
  1250.             array = new NSArray();
  1251.             ssrDeliciousHelper._addUniqueToArray( data[ prop ], array );
  1252.             tags.setProperty( type, array );
  1253.          }
  1254.          tagArr.appendElement( tags, false );
  1255.          cb.onload( tagArr );
  1256.       };
  1257.       
  1258.       var onerror = function( event ) {
  1259.          cb.onerror( event );
  1260.       };
  1261.  
  1262.       this._post( queryString, onload, onerror );
  1263.    },
  1264.    
  1265.    allBundles: function (cb) {
  1266.        var onerror = function(event) {
  1267.          cb.onerror( event );
  1268.        };
  1269.  
  1270.        var onload = function(event) {
  1271.          try {
  1272.          // for all bookmarks, set the first element in the result set to the total
  1273.          // number of items if all items were downloaded at once.
  1274.          
  1275.          if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  1276.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  1277.             cb.onerror(event);
  1278.          } else {
  1279.  
  1280.           var doc = event.target.responseXML;
  1281.           //yDebug.print(event.target.responseText);
  1282.           var nodes = doc.getElementsByTagName('bundle');
  1283.  
  1284.           yDebug.print("del nodelen for bundles:" + nodes.length, YB_LOG_MESSAGE); 
  1285.  
  1286.           var posts = new NSArray();
  1287.  
  1288.           var node, data;
  1289.         
  1290.           for (var i=0; i < nodes.length; i++) {
  1291.             node = nodes.item(i);
  1292.             var name = node.getAttribute("name");
  1293.             var tags = node.getAttribute("tags");
  1294.             var bundle = new HashPropertyBag();
  1295.             var nsTags = new NSString();
  1296.             nsTags.data = tags;
  1297.             var nsName = new NSString();
  1298.             nsName.data = name;
  1299.             
  1300.             bundle.setProperty("name", nsName);
  1301.             bundle.setProperty("tags", nsTags);
  1302.             
  1303.           /*  var nsTags = ybookmarksUtils.jsArrayToNS(tags.split(" "));
  1304.             //posts.appendElement(bundle, false);
  1305.             var bundle = { name: name,
  1306.                             tags: nsTags };*/
  1307.             posts.appendElement(bundle, false);
  1308.             
  1309.             //posts.push(bundle);
  1310.           }
  1311.           cb.onload( posts );
  1312.         }
  1313.         } catch (e) {
  1314.           yDebug.print("ERROR PROCESSING BUNDLE:" + e);
  1315.         }  
  1316.       };
  1317.       
  1318.       this._post(DEL_ALL_BUNDLES, onload, onerror);
  1319.      
  1320.    },
  1321.    
  1322.    setBundle: function (aBundle, aTags, cb) {
  1323.      var onerror = function(event) {
  1324.        cb.onerror( (ssrDeliciousHelper._parseErrorResponseFromDelicious(event)).data );
  1325.        try {
  1326.          yDebug.print( "Error Response (set bundle): " + event.target.status + ", " + event.target.statusText , YB_LOG_MESSAGE );
  1327.        } catch(e) { }  
  1328.         
  1329.        cb.onerror( event );
  1330.      };
  1331.  
  1332.      var onload = function(event) {
  1333.        // for all bookmarks, set the first element in the result set to the total
  1334.        // number of items if all items were downloaded at once.
  1335.  
  1336.        var retval = ssrDeliciousHelper._parseResponseFromDelicious( event );
  1337.   
  1338.        if ( retval.result ) {
  1339.          cb.onload( retval.data );        
  1340.        } else {
  1341.          cb.onerror( retval.data );
  1342.          yDebug.print( "Error Response (set bundle): " + event.target.status + ", " + event.target.statusText, YB_LOG_MESSAGE );
  1343.        } 
  1344.      };
  1345.     
  1346.      var queryString = DEL_SET_BUNDLE + 
  1347.                         "bundle=" + encodeURIComponent( aBundle ) + 
  1348.                         "&tags=" + encodeURIComponent( aTags );
  1349.  
  1350.      this._post(queryString, onload, onerror);
  1351.  
  1352.    },
  1353.    
  1354.    deleteBundle: function (aBundle, cb) {
  1355.      
  1356.       var onerror = function(event) {
  1357.         cb.onerror( (ssrDeliciousHelper._parseErrorResponseFromDelicious(event)).data );
  1358.         try {
  1359.           yDebug.print( "Error Response (del bundle): " + event.target.status + ", " + event.target.statusText , YB_LOG_MESSAGE );
  1360.         } catch(e) { }  
  1361.  
  1362.         cb.onerror( event );
  1363.       };
  1364.  
  1365.       var onload = function(event) {
  1366.         // for all bookmarks, set the first element in the result set to the total
  1367.         // number of items if all items were downloaded at once.
  1368.  
  1369.         var retval = ssrDeliciousHelper._parseResponseFromDelicious( event );
  1370.  
  1371.         if ( retval.result ) {
  1372.           cb.onload( retval.data );        
  1373.         } else {
  1374.           cb.onerror( retval.data );
  1375.           yDebug.print( "Error Response (del bundle): " + event.target.status + ", " + event.target.statusText, YB_LOG_MESSAGE );
  1376.         } 
  1377.       };
  1378.  
  1379.       var queryString = DEL_DELETE_BUNDLE + "bundle=" + encodeURIComponent( aBundle );
  1380.  
  1381.       this._post(queryString, onload, onerror);
  1382.      /*
  1383.        var onerror = function(event) {
  1384.          cb.onerror( event );
  1385.        };
  1386.  
  1387.        var onload = function(event) {
  1388.          try {
  1389.          // for all bookmarks, set the first element in the result set to the total
  1390.          // number of items if all items were downloaded at once.
  1391.  
  1392.          if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  1393.             yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  1394.             cb.onerror(event);
  1395.          } else {
  1396.  
  1397.           var doc = event.target.responseXML;
  1398.           //yDebug.print(event.target.responseText);
  1399.           var nodes = doc.getElementsByTagName('bundle');
  1400.  
  1401.           yDebug.print("del nodelen for bundles:" + nodes.length, YB_LOG_MESSAGE); 
  1402.  
  1403.           var posts = new NSArray();
  1404.  
  1405.           var node, data;
  1406.  
  1407.           for (var i=0; i < nodes.length; i++) {
  1408.             node = nodes.item(i);
  1409.             var name = node.getAttribute("name");
  1410.             var tags = node.getAttribute("tags");
  1411.             var bundle = new HashPropertyBag();
  1412.  
  1413.             var nsTags = new NSString();
  1414.             nsTags.data = tags;
  1415.             var nsName = new NSString();
  1416.             nsName.data = name;
  1417.  
  1418.             bundle.setProperty("name", nsName);
  1419.             bundle.setProperty("tags", nsTags);
  1420.             posts.appendElement(bundle, false);
  1421.           }
  1422.           cb.onload( posts );
  1423.         }
  1424.         } catch (e) {
  1425.           yDebug.print("ERROR PROCESSING BUNDLE:" + e);
  1426.         }  
  1427.       };
  1428.  
  1429.       this._post(DEL_ALL_BUNDLES, onload, onerror);
  1430. */
  1431.    },
  1432.  
  1433.    get login_url() { return LOGIN_URL + DEL_UA_STRING; },
  1434.  
  1435.    get register_url() { return REGISTER_URL + DEL_UA_STRING; },
  1436.  
  1437.    get service_name() { return SERVICE_NAME; },
  1438.  
  1439.    get home_url() { 
  1440.      if(PREFDOMAIN == "fallback") {
  1441.         if(this.cred.user && this.cred.cookie.host ==  DOTCOMDOMAIN) {
  1442.             return BETA_HOME_URL;
  1443.         }
  1444.      }               
  1445.      return HOME_URL; 
  1446.    },
  1447.    
  1448.    getUserName: function() {
  1449.      return this.cred.user;
  1450.    },
  1451.  
  1452.    QueryInterface: function(aIID)
  1453.    {
  1454.       if (!aIID.equals(nsISocialStore) &&    
  1455.           !aIID.equals(nsISupports))
  1456.          throw Components.results.NS_ERROR_NO_INTERFACE;
  1457.       return this;
  1458.    }
  1459. };
  1460.  
  1461. var ssrDeliciousHelper = {
  1462.  
  1463.    _getBMDS: function() {
  1464.       if (this.BMDS == null) {
  1465.          this.BMDS = this.RDF.GetDataSource("rdf:bookmarks");
  1466.       }
  1467.  
  1468.       return this.BMDS;
  1469.    },
  1470.  
  1471.    _URL2Icon: function (url) {
  1472.       var urlLiteral = this.RDF.GetLiteral(url);
  1473.  
  1474.       var bmResources =
  1475.          this._getBMDS().GetSources(this.RDF.GetResource(this.NC_NS+"URL"),
  1476.                                     urlLiteral,
  1477.                                     true);
  1478.  
  1479.       while (bmResources.hasMoreElements()) {
  1480.          var bmResource = bmResources.getNext();
  1481.          
  1482.          var icon =
  1483.             this._getBMDS().GetTarget(bmResource,
  1484.                                 this.RDF.GetResource(this.NC_NS + "Icon"),
  1485.                                 true);
  1486.          if (icon) {
  1487.           icon = icon.QueryInterface(this.kRDFLITIID).Value;
  1488.             return icon;
  1489.          }
  1490.       }
  1491.  
  1492.       return null;
  1493.    },
  1494.    
  1495.    /**
  1496.     * Determines if there were any errors in an XMLHttpRequest.
  1497.     * @param event the response event.
  1498.     * @return true if the response is valid, false if there was an error.
  1499.     */
  1500.    _isValidResponse: function(event, json) {
  1501.       if (event.target.status != 200) {
  1502.          return false;
  1503.       }
  1504.  
  1505.       if (!json) {
  1506.          try { // handle malformed XML
  1507.             var doc = event.target.responseXML;
  1508.  
  1509.             if (!doc || !doc.firstChild) {
  1510.                yDebug.print("No document", YB_LOG_MESSAGE);
  1511.                return false;
  1512.             }
  1513.  
  1514.             if (doc.firstChild.tagName == "error") {
  1515.                yDebug.print("Error response:" + doc, YB_LOG_MESSAGE);
  1516.                return false;
  1517.             }
  1518.          } catch (e) {
  1519.             yDebug.print("Failed to parse response:" + e, YB_LOG_MESSAGE);
  1520.             return false;
  1521.          }
  1522.       }
  1523.       
  1524.       return true;
  1525.    },
  1526.    
  1527.    /**
  1528.     * Loads a set of bookmarks.
  1529.     * @param event the response event.
  1530.     * @return the total count of bookmarks in the remote list. This is not
  1531.     * necessarily equal to the amount of bookmarks being downloaded now.
  1532.     */
  1533.    _loadBookmarks: function(event, shouldGetTotal) {
  1534.  
  1535.     if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  1536.        yDebug.print("delfailed:" + event.target.status, YB_LOG_MESSAGE);
  1537.        return false;
  1538.      }
  1539.  
  1540.      var doc = event.target.responseXML;
  1541.      //yDebug.print(event.target.responseText);
  1542.      var nodes = doc.getElementsByTagName('post');
  1543.       
  1544.      yDebug.print("del nodelen:" + nodes.length, YB_LOG_MESSAGE); 
  1545.  
  1546.      var posts = new NSArray();
  1547.   
  1548.      var node, data;
  1549.      if ( shouldGetTotal ) {
  1550.        // first element is always total number of elements
  1551.        var total = doc.documentElement.getAttribute( "total" );
  1552.        data = new HashPropertyBag();
  1553.        data.setProperty( "total", total );
  1554.        posts.appendElement( data, false );
  1555.      }
  1556.      
  1557.      for (var i = 0; i < nodes.length; i++) {
  1558.        node = nodes.item(i);
  1559.        data = ssrDeliciousHelper._getHashFromDeliciousNode( node );
  1560.        posts.appendElement(data, false);
  1561.      }
  1562.  
  1563.      return posts;
  1564.  
  1565.    },
  1566.    
  1567.    /**
  1568.     * Takes a date and time string in the API format and converts it to a
  1569.     * number format (microseconds).
  1570.     * @param timeStr string representation of a given time, in the format
  1571.     * YYYY-MM-DDThh:mm:ssZ.
  1572.     * @return time in microseconds.
  1573.     */
  1574.    _getTimeFromString: function(timeStr) {
  1575.       var time = timeStr;
  1576.       
  1577.       time = time.replace(/-/g, "/");
  1578.       time = time.replace("T", " ");
  1579.       time = time.replace("Z", " ");
  1580.       
  1581.       time += "GMT"; //  times returned by the del.icio.us API are in GMT, so we must treat them as such
  1582.       return Date.parse(time) * 1000;
  1583.    },
  1584.    
  1585.    /**
  1586.     * Formats the given time to the date and time string required by the API.
  1587.     * @param time time in milliseconds.
  1588.     * @return string representation of the given time, in the format
  1589.     * YYYY-MM-DDThh:mm:ssZ.
  1590.     */
  1591.    _formatTime: function(time) {
  1592.       var date = new Date();
  1593.       var timeString;
  1594.       var month;
  1595.       var day;
  1596.       var hours;
  1597.       var minutes;
  1598.       var seconds;
  1599.        
  1600.       date.setTime(time);
  1601.       month = _pad2Digits(date.getMonth() + 1);
  1602.       day = _pad2Digits(date.getDate());
  1603.       hours = _pad2Digits(date.getHours());
  1604.       minutes = _pad2Digits(date.getMinutes());
  1605.       seconds = _pad2Digits(date.getSeconds());
  1606.       
  1607.       timeString = 
  1608.          date.getFullYear() + "-" + month + "-" + day + "T" + hours + ":" +
  1609.          minutes + ":" + seconds + "Z";
  1610.          
  1611.       return timeString;
  1612.    },
  1613.    
  1614.    /**
  1615.     * Returns a string representation of a number, with a fixed length of 2.
  1616.     * @param the number to convert to a string of size 2.
  1617.     * @return string of size 2 padded with a zero to the left if necessary.
  1618.     */
  1619.    _pad2Digits: function(number) {
  1620.       var str = (number + 100) + "";
  1621.       
  1622.       return str.substring(1,3);
  1623.    },
  1624.  
  1625.    /**
  1626.     *  Extract the bookmark url from a XHR url.
  1627.     *  
  1628.     *  @param  xhe the XHR url which sent to the server
  1629.     *  @return url the bookmark url
  1630.     **/   
  1631.    _getBookmarkURLFromXHR : function (request) {
  1632.       
  1633.      var path = request.channel.originalURI.path;
  1634.      var startAttr = "url=";
  1635.      var endAttr = "&";
  1636.      var startPos = path.indexOf(startAttr);
  1637.      var url;
  1638.      if (startPos != -1) { //bookmarks
  1639.        var endPos = path.indexOf("&", startPos);
  1640.        
  1641.        if (endPos != -1) {
  1642.          url = path.substring(startPos + startAttr.length, endPos);  
  1643.        } else {
  1644.          url = path.substring(startPos + startAttr.length);
  1645.        }
  1646.      } else { // bundles
  1647.        startAttr = "bundle=";
  1648.        startPos = path.indexOf(startAttr);
  1649.        endPos = path.indexOf("&", startPos);
  1650.         if (endPos != -1) {
  1651.           url = path.substring(startPos + startAttr.length, endPos);  
  1652.         } else {
  1653.           url = path.substring(startPos + startAttr.length);
  1654.         }
  1655.         url = YB_BUNDLE_URI + url;
  1656.      }
  1657.  
  1658.      return decodeURIComponent(url);
  1659.    },
  1660.  
  1661.    _addUniqueToArray: function( inArr, outArr ) {
  1662.       var i, str, toAdd, strObj;
  1663.       for( i = 0; i < inArr.length; ++i ) {
  1664.          if( typeof inArr[ i ] == 'string' ) {
  1665.             str = inArr[ i ].toLowerCase();
  1666.          }
  1667.          else {
  1668.             str = inArr[ i ];
  1669.          }
  1670.          iter = outArr.enumerate();
  1671.          toAdd = true;
  1672.          while( iter.hasMoreElements() ) {
  1673.             if( iter.getNext().data == str ) {
  1674.                toAdd = false;
  1675.                break;
  1676.             }
  1677.          }
  1678.          if( toAdd ) {
  1679.             strObj = new NSString();
  1680.             strObj.data = str;
  1681.             outArr.appendElement( strObj, false );
  1682.          }
  1683.       }
  1684.    },
  1685.  
  1686.    /**
  1687.     * If notes has microsummary embedded in it, extract the microsummary uri and set it as another attribute
  1688.     * in the hash.
  1689.     */
  1690.    _parseNotesForMicrosummary: function(notes, hash) {
  1691.      var arr = null;
  1692.      if ( ( arr =  notes.match ( /\[microsummary:\s*([^\]]+)\]\s*/ ) ) ) {
  1693.        notes = notes.replace( /\[microsummary:[^\]]+\]\s*/, "" );
  1694.        hash.setProperty( "microsummary", arr[1] );
  1695.      }
  1696.  
  1697.      return notes;
  1698.    },
  1699.  
  1700.    /**
  1701.     * Read the response from Delicious and populate the hash
  1702.     *
  1703.     */
  1704.   _getHashFromDeliciousNode: function(node) {
  1705.     var title = node.getAttribute('description');
  1706.     var notes = node.getAttribute('extended');
  1707.     var tagstr = node.getAttribute('tag');
  1708.     var href = node.getAttribute('href');
  1709.     var hash = node.getAttribute('hash');
  1710.     var metahash = node.getAttribute('meta');
  1711.     var shared = node.getAttribute('shared');
  1712.  
  1713.  
  1714.     if (!notes ) {
  1715.        notes = "";
  1716.     }
  1717.  
  1718.     if (shared && shared == "no") {
  1719.       shared = "false";
  1720.     } else {
  1721.       shared = "true";
  1722.     }
  1723.  
  1724.     var shortcuts = tagstr.match ( /\s*shortcut:([^\s]+)/ );
  1725.     var shortcut = "";
  1726.     if ( shortcuts ) {
  1727.       yDebug.print ( "Shortcut matched" );
  1728.       shortcut = shortcuts[1];
  1729.     }
  1730.  
  1731.     var data = new HashPropertyBag();
  1732.     
  1733.     try {
  1734.         notes = ssrDeliciousHelper._parseNotesForMicrosummary( notes, data );
  1735.     } catch(e) {
  1736.         //do nothing
  1737.     }
  1738.     
  1739.     data.setProperty("title", title);
  1740.     data.setProperty("notes", notes);
  1741.     data.setProperty("tags", tagstr);
  1742.     data.setProperty("url", href);
  1743.     data.setProperty("hash", hash);
  1744.     data.setProperty("metahash", metahash);
  1745.     data.setProperty("shared", shared);
  1746.     data.setProperty("shortcut", shortcut);
  1747.     
  1748.     try {
  1749.         if(ybookmarksUtils.getFFMajorVersion() > 2) {
  1750.     //        yDebug.print ("href: "+href, YB_LOG_MESSAGE );
  1751.                var faviconService = Components.classes["@mozilla.org/browser/favicon-service;1"]
  1752.                                            .getService(Components.interfaces.nsIFaviconService);
  1753.             var ioservice = Components.classes["@mozilla.org/network/io-service;1"]
  1754.                                           .getService(Components.interfaces.nsIIOService);
  1755.             var pageURI = ioservice.newURI(href, null, null);
  1756.             var faviconLink = faviconService.getFaviconImageForPage(pageURI);
  1757.     
  1758.             //        yDebug.print ("favicon: "+faviconLink.spec, YB_LOG_MESSAGE );
  1759.             if(faviconLink.spec) {
  1760.                 data.setProperty("icon", faviconLink.spec);
  1761.             }
  1762.             else {
  1763.                 data.setProperty("icon", "");
  1764.             }
  1765.         }
  1766.         else {
  1767.             data.setProperty("icon", ssrDeliciousHelper._URL2Icon(href));
  1768.         }
  1769.     } catch(e) {
  1770.         data.setProperty("icon", "");
  1771.     }
  1772.  
  1773.  
  1774.     var updateTime = node.getAttribute('time');
  1775.     if (updateTime) {
  1776.         updateTime= ssrDeliciousHelper._getTimeFromString(updateTime);
  1777.         data.setProperty("update_time", updateTime);
  1778.         data.setProperty("add_time", updateTime);
  1779.     }
  1780.  
  1781.     return data;
  1782.  
  1783.   },
  1784.  
  1785.   _parseResponseFromDelicious: function(event) {
  1786.  
  1787.     var bookmarkUrl = ssrDeliciousHelper._getBookmarkURLFromXHR (event.target); 
  1788.     var resultArray = new NSArray();
  1789.     var data = new HashPropertyBag();      
  1790.     data.setProperty("url", bookmarkUrl);
  1791.     data.setProperty("status", event.target.status);
  1792.     data.setProperty("statusText", event.target.statusText);    
  1793.     resultArray.appendElement(data, false);
  1794.  
  1795.     if (!ssrDeliciousHelper._isValidResponse(event, false)) {
  1796.       
  1797.       return { result: false, 
  1798.                data: resultArray
  1799.              };
  1800.     }
  1801.                  
  1802.     var doc = event.target.responseXML;         
  1803.     var nodes = doc.getElementsByTagName( "result" );
  1804.   
  1805.     
  1806.     var resultNode = nodes.item( 0 );
  1807.     var resultCode = resultNode.getAttribute( "code" );
  1808.     var result;
  1809.     
  1810.     if (resultCode) {// for bookmarks
  1811.       result = resultCode == "done";  
  1812.     } else if (resultNode.firstChild) { // for bundles
  1813.       result = (resultNode.firstChild.nodeValue == "ok" || resultNode.firstChild.nodeValue == "done");
  1814.     } else {
  1815.       result = false;
  1816.     }
  1817.     return { result: result,
  1818.              data: resultArray
  1819.            };
  1820.   },
  1821.  
  1822.   _parseErrorResponseFromDelicious: function(event) {
  1823.     var resultArray = new NSArray();
  1824.     if (!event.target) {
  1825.     return resultArray;
  1826.     }
  1827.     
  1828.     var bookmarkUrl = ssrDeliciousHelper._getBookmarkURLFromXHR (event.target); 
  1829.     try {
  1830.       var data = new HashPropertyBag();      
  1831.       data.setProperty("url", bookmarkUrl);
  1832.       data.setProperty("status", event.target.status);
  1833.       resultArray.appendElement(data, false);
  1834.     }
  1835.     catch(e){ }
  1836.     return resultArray;
  1837.   }
  1838.  
  1839. };
  1840.  
  1841. ssrDeliciousHelper.RDF =
  1842. (Components.classes['@mozilla.org/rdf/rdf-service;1'].
  1843.  getService(Components.interfaces.nsIRDFService));
  1844.  
  1845. ssrDeliciousHelper.NC_NS = "http://home.netscape.com/NC-rdf#";
  1846. ssrDeliciousHelper.kRDFLITIID = Components.interfaces.nsIRDFLiteral;
  1847.  
  1848.  
  1849. /***********************************************************
  1850. class factory
  1851.  
  1852. This object is a member of the global-scope Components.classes.
  1853. It is keyed off of the contract ID. Eg:
  1854.  
  1855. mySocialStore = Components.classes["@yahoo.com/socialstore/delicious;1"].
  1856.                           createInstance(Components.interfaces.nsISocialStore);
  1857.  
  1858. ***********************************************************/
  1859. var SSRDeliciousFactory = {
  1860.    createInstance: function (aOuter, aIID)
  1861.    {
  1862.       if (aOuter != null)
  1863.          throw Components.results.NS_ERROR_NO_AGGREGATION;
  1864.       return (new SSRDelicious()).QueryInterface(aIID);
  1865.    }
  1866. };
  1867.  
  1868. /***********************************************************
  1869. module definition (xpcom registration)
  1870. ***********************************************************/
  1871. var SSRDeliciousModule = {
  1872.    _firstTime: true,
  1873.    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
  1874.    {
  1875.       aCompMgr = aCompMgr.
  1876.       QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1877.       aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, 
  1878.                                        CONTRACT_ID, aFileSpec, aLocation, aType);
  1879.    },
  1880.  
  1881.    unregisterSelf: function(aCompMgr, aLocation, aType)
  1882.    {
  1883.       aCompMgr = aCompMgr.
  1884.       QueryInterface(Components.interfaces.nsIComponentRegistrar);
  1885.       aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  1886.    },
  1887.    
  1888.    getClassObject: function(aCompMgr, aCID, aIID)
  1889.    {
  1890.       if (!aIID.equals(Components.interfaces.nsIFactory))
  1891.          throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  1892.  
  1893.       if (aCID.equals(CLASS_ID))
  1894.          return SSRDeliciousFactory;
  1895.  
  1896.       throw Components.results.NS_ERROR_NO_INTERFACE;
  1897.    },
  1898.  
  1899.    canUnload: function(aCompMgr) { return true; }
  1900. };
  1901.  
  1902. /***********************************************************
  1903. module initialization
  1904.  
  1905. When the application registers the component, this function
  1906. is called.
  1907. ***********************************************************/
  1908. function NSGetModule(aCompMgr, aFileSpec) { return SSRDeliciousModule; }
  1909.